/*
 * Created on 2004-07-15
 */
package org.opencube.oms.mapping;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;

import org.apache.log4j.Logger;
import org.opencube.data.BoundVariable;
import org.opencube.data.CallResult;
import org.opencube.data.RecordSet;
import org.opencube.oms.OMSElement;
import org.opencube.oms.OMSStructure;
import org.opencube.oms.RecordsetConverter;
import org.opencube.oms.meta.ComplexAttribute;
import org.opencube.oms.meta.OMSMetaData;
import org.opencube.util.Constants;
import org.opencube.util.Util;
/**
 * @author <a href="mailto:maciek@fingo.pl">Maciej Mroczko - FINGO</a>
 *
 * TODO comments
 */
public class Finder
{
	private Logger logger = Logger.getLogger( Finder.class );
	
	public static final String TEMPORARY_NUMBER_TABLE_NAME = "TMP_NUMBER";
	public static final String TEMPORARY_STRING_TABLE_NAME = "TMP_STRING";
	
	private static final String RESOURCE_FIND_ELEMENT_BY_ID = "findElementById";
	private static final String RESOURCE_FIND_ELEMENT_BY_BOOKMARK = "findElementByBookmark";
	private static final String RESOURCE_FIND_ELEMENT_BY_GUID = "findElementByGuid";
	
	private static final String RESOURCE_FIND_ELEMENT_BY_ID_WITH_CHILDREN = "findElementByIdWithChildren";
	private static final String RESOURCE_FIND_ELEMENT_BY_BOOKMARK_WITH_CHILDREN = "findElementByBookmarkWithChildren";
	private static final String RESOURCE_FIND_ELEMENT_BY_GUID_WITH_CHILDREN = "findElementByGuidWithChildren";
	
	private static final String RESOURCE_GET_ELEMENT_WITH_LANG_BY_ID = "getElementWithLangById";

	protected DataStore store = null;
	
	public Finder( DataStore store )
	{
		this.store = store;
	}
	
	private String loadResource( Class cl, String resource )
	throws FileNotFoundException, IOException
	{
		resource = getResourceKey( resource );
		logger.debug( "load resource " + resource + " FOR " + cl.getName() );
		return Util.getInputStreamAsString( cl.getResourceAsStream( resource ) );
	}
	
	public OMSElement[] findElementsByParentAttribute( Entity parent, String attributeName, boolean withChildren )
	throws Exception
	{
		ComplexAttribute cat = getMetaData().getComplexAttributeFromHierarchy( getMetaData().getScheme( parent.getElement().getNamespacePath(), parent.getElement().getSchemeName() ), attributeName );
		if( cat.isAssociation() )
		{
			return findAssociated( parent, attributeName, withChildren ); 
		}
		else
		{
			return findComposed( parent, attributeName, withChildren ); 
		}
	}

	public OMSElement findElementById( String id, boolean withChildren ) throws Exception
	{
		BoundVariable[] vars = new BoundVariable[]
		{
			new BoundVariable( "in_int_id", new Integer( id ) ),
			new BoundVariable( "out_cursor_tree", null ),
		};
		
		OMSStructure structure = getStructureByResource( Finder.class, withChildren ? RESOURCE_FIND_ELEMENT_BY_ID_WITH_CHILDREN : RESOURCE_FIND_ELEMENT_BY_ID, null, vars );
		
		OMSElement element = structure.getElementById( id + "" );
		
		return element;
	}
	
	public OMSElement[] findElementsByIds( String[] elementIds )
	throws Exception
	{
	    StringBuffer sb = new StringBuffer();
	    for( int i = 0; i < elementIds.length; i++ )
	    {
	        if( i > 0 )
	        {
	            sb.append( ',' );
	        }
	        sb.append( "'" );
	        sb.append( elementIds[ i ] );
	        sb.append( "'" );
	    }
		HashMap<String, String> placeholder = new HashMap<String, String>( 1 );
		placeholder.put( "in_ids_list", sb.toString() );
		BoundVariable[] bindVariables = new BoundVariable[]{ new BoundVariable( "out_cursor_tree", null ) };
		OMSStructure tree = getStructureByResource( Finder.class, "findElementsByIds.sql", placeholder, bindVariables );
		return tree.getElements();
	}
	
	public OMSElement findElementByGuid( String guid, boolean withChildren ) throws Exception
	{
		BoundVariable varId = new BoundVariable( "out_int_id", null );
		BoundVariable[] vars = new BoundVariable[]
		{
			new BoundVariable( "in_string_guid", guid ),
			varId,
			new BoundVariable( "out_cursor_tree", null ),
		};
		
		OMSStructure structure = getStructureByResource( Finder.class, withChildren ? RESOURCE_FIND_ELEMENT_BY_GUID_WITH_CHILDREN : RESOURCE_FIND_ELEMENT_BY_GUID, null, vars );
		
		OMSElement element = varId.getValue() != null ? structure.getElementById( varId.getValue().toString() ) : null;
		
		return element;
	}
	
	protected OMSElement findElementWithLanguageById( String elementId, String languageAttributeId )
	throws Exception
	{
		BoundVariable[] vars = new BoundVariable[]
		{
			new BoundVariable( "out_cursor_tree", null ),
			new BoundVariable( "in_int_elm_id", new Integer( elementId ) ),
			new BoundVariable( "in_int_lang_att_id", new Integer( languageAttributeId ) ),
		};
		OMSStructure tree = getStructureByResource( Finder.class, RESOURCE_GET_ELEMENT_WITH_LANG_BY_ID, null, vars );
		return tree.getElementById( elementId );
	}
	
	public OMSElement findElementByBookmark( String bookmark, boolean withChildren ) throws Exception
	{
		BoundVariable[] vars = new BoundVariable[]
		{
			new BoundVariable( "in_string_bookmark", bookmark ),
			new BoundVariable( "out_cursor_tree", null ),
		};
		
		OMSStructure structure = getStructureByResource( Finder.class, withChildren ? RESOURCE_FIND_ELEMENT_BY_BOOKMARK_WITH_CHILDREN : RESOURCE_FIND_ELEMENT_BY_BOOKMARK, null, vars );
		
		OMSElement element = structure.getElementByBookmark( bookmark );
		
		return element;
	}
	
	protected OMSMetaData getMetaData() throws Exception
	{
		return store.getOMSData().getMetaData();
	}
	
	protected OMSStructure getStructureByResource( Class cl, String resourceName, HashMap placeHolders, BoundVariable[] boundVariables )
	throws Exception
	{
		return store.getOMSData().getOMSStructureByStatement( loadResource( cl, resourceName ), placeHolders, boundVariables, getNamespace( cl ), getResourceKey( resourceName ) );
	}
	
	protected CallResult executeCallByResource( Class cl, String resourceName, HashMap<String, String> placeHolders, BoundVariable[] boundVariables )
	throws Exception
	{
		return store.getOMSData().executeCall( loadResource( cl, resourceName ), placeHolders, boundVariables, getNamespace( cl ), getResourceKey( resourceName ) );
	}

	private OMSElement[] findAssociated( Entity parent, String catName, boolean withChildren )
	throws Exception
	{
		BoundVariable elmRS = new BoundVariable( "out_cursor_elements", null );
		BoundVariable svlRS = new BoundVariable( "out_cursor_simplevalues", null );
		BoundVariable uvlRS = new BoundVariable( "out_cursor_uniquevalues", null );
		BoundVariable cvlRS = new BoundVariable( "out_cursor_clobvalues", null );
		BoundVariable bvlRS = new BoundVariable( "out_cursor_blobvalues", null );
		BoundVariable assRS = new BoundVariable( "out_cursor_associations", null );
		BoundVariable cmpRS = new BoundVariable( "out_cursor_compositions", null );
		BoundVariable[] vars = new BoundVariable[]
		{
			elmRS,
			svlRS,
			uvlRS,
			cvlRS,
			bvlRS,
			assRS,
			new BoundVariable( "in_int_elm_id", Integer.parseInt( parent.getElementId() ) ),
			new BoundVariable( "in_int_cat_id", new Integer( getMetaData().getComplexAttributeFromHierarchy( getMetaData().getScheme( parent.getElement().getNamespacePath(), parent.getElement().getSchemeName() ), catName ).getId() ) ),
		};
		if( withChildren )
		{
			ArrayList<BoundVariable> list = new ArrayList<BoundVariable>();
			list.addAll( Arrays.asList( vars ) );
			list.add( cmpRS );
			vars = list.toArray( new BoundVariable[ list.size() ] );
 		}
		
		executeCallByResource( Finder.class, withChildren ? "findAssociatedElementsWithChildren.sql" : "findAssociatedElements.sql", null, vars );
		OMSStructure tree = new OMSStructure();
		RecordsetConverter.loadElements( tree, (RecordSet) elmRS.getValue(), getMetaData() );
		RecordsetConverter.loadSimplevalues( tree, (RecordSet) svlRS.getValue(), getMetaData() );
		RecordsetConverter.loadUniquevalues( tree, (RecordSet) uvlRS.getValue(), getMetaData() );
		RecordsetConverter.loadClobvalues( tree, (RecordSet) cvlRS.getValue(), getMetaData() );
		RecordsetConverter.loadBlobvalues( tree, (RecordSet) bvlRS.getValue(), getMetaData() );
		if( withChildren )
		{
			RecordsetConverter.loadCompositions( tree, (RecordSet) cmpRS.getValue(), getMetaData() );
		}
		RecordsetConverter.loadAssociations( tree, (RecordSet) assRS.getValue(), getMetaData() );
		
		OMSElement e = tree.getElementById( parent.getElementId() );
		return e.getJoinedElements( catName );
	}
	private OMSElement[] findComposed( Entity parent, String catName, boolean withChildren )
	throws Exception
	{
		BoundVariable elmRS = new BoundVariable( "out_cursor_elements", null );
		BoundVariable svlRS = new BoundVariable( "out_cursor_simplevalues", null );
		BoundVariable uvlRS = new BoundVariable( "out_cursor_uniquevalues", null );
		BoundVariable cvlRS = new BoundVariable( "out_cursor_clobvalues", null );
		BoundVariable bvlRS = new BoundVariable( "out_cursor_blobvalues", null );
		BoundVariable cmpRS = new BoundVariable( "out_cursor_compositions", null );
		BoundVariable[] vars = new BoundVariable[]
		{
			elmRS,
			svlRS,
			uvlRS,
			cvlRS,
			bvlRS,
			cmpRS,
			new BoundVariable( "in_int_elm_id", Integer.parseInt( parent.getElementId() ) ),
			new BoundVariable( "in_int_cat_id", new Integer( getMetaData().getComplexAttributeFromHierarchy( getMetaData().getScheme( parent.getElement().getNamespacePath(), parent.getElement().getSchemeName() ), catName ).getId() ) ),
		};
		
		executeCallByResource( Finder.class, withChildren ? "findComposedElementsWithChildren.sql" : "findComposedElements.sql", null, vars );
		OMSStructure tree = new OMSStructure();
		RecordsetConverter.loadElements( tree, (RecordSet) elmRS.getValue(), getMetaData() );
		RecordsetConverter.loadSimplevalues( tree, (RecordSet) svlRS.getValue(), getMetaData() );
		RecordsetConverter.loadUniquevalues( tree, (RecordSet) uvlRS.getValue(), getMetaData() );
		RecordsetConverter.loadClobvalues( tree, (RecordSet) cvlRS.getValue(), getMetaData() );
		RecordsetConverter.loadBlobvalues( tree, (RecordSet) bvlRS.getValue(), getMetaData() );
		RecordsetConverter.loadCompositions( tree, (RecordSet) cmpRS.getValue(), getMetaData() );
		
		OMSElement e = tree.getElementById( parent.getElementId() );
		return e.getJoinedElements( catName );
	}
	
	protected Integer getCatId( String nspName, String scmName, String catName )
	throws Exception
	{
		return new Integer( getMetaData().getComplexAttributeFromHierarchy( getMetaData().getScheme( nspName, scmName ), catName ).getId() );
	}
	
	protected Integer getSatId( String nspName, String scmName, String satName )
	throws Exception
	{
		return new Integer( getMetaData().getSimpleAttributeFromHierarchy( getMetaData().getScheme( nspName, scmName ), satName ).getId() );
	}
	
	private static String getNamespace( Class c )
	{
		return c.getPackage().getName();
	}
	 
	public static void main( String[] args )
	{
		System.out.println( getNamespace( String.class ) );
	}
	
	private static String getResourceKey( String s )
	{
		return s.indexOf( Constants.STR_DOT ) == -1 ? s + ".res" : s;
	}
}
